home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / demos / GL / flip / sbinteg.c < prev    next >
Encoding:
Text File  |  1994-08-02  |  32.8 KB  |  1,292 lines

  1. /*
  2.  * Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. /*
  18.  * sbinteg.c
  19.  *        Spaceball Integration Module
  20.  *
  21.  * Written By Gianni Mariani 10-Jul-1989
  22.  *
  23.  *    
  24.  * Copyright (C) 1989 Spatial Systems Inc. All Rights Reserved.
  25.  * Spaceball is a registered trademark of Spatial Systems Inc.
  26.  *
  27.  * RESTRICTIONS
  28.  *
  29.  * This module may be used and distributed freely provided the
  30.  * routines are used exclusively in conjunction with a Spaceball (TM). No
  31.  * portion may be used for other purposes without prior written permission
  32.  * from Spatial Systems Inc. This notice must accompany any full or partial
  33.  * copies.
  34.  *
  35.  * PURPOSE
  36.  *
  37.  * This module contains handling routines for Spaceball events.  This
  38.  * is intended to be modified by each application developer to suit
  39.  * their own needs.  It is suggested however that the keypad functionality
  40.  * be left somewhat the same as it is here so that users become accustomed
  41.  * to the functionality and hence reduce the learning time between systems
  42.  * that they use.
  43.  *
  44.  * HOW
  45.  *
  46.  * Programming Spaceball on an Iris workstation
  47.  * 
  48.  *     Iris workstations allow access to the Spaceball through the 
  49.  * Silicon Graphics GL event queue system.  The Spaceball interface 
  50.  * has been implemented as 7 new 'delta' devices and 9 new button
  51.  * devices.  In addition there are a number of new calls to 
  52.  * allow you to control Spaceball.
  53.  *
  54.  *     This module provides all the nessasary code to manipulate the
  55.  * Spaceball GL interface.  This module will present the information
  56.  * to the application you wish to integrate in an easy to use form.
  57.  * You can use the Spaceball GL interface directly to perform other
  58.  * operations not provided in this module.
  59.  * 
  60.  * This module's interface consists of 5 routines that can be called from
  61.  * well defined places in the source.  These are :
  62.  * 
  63.  *     sbInit()        - Initialize module sbinteg
  64.  *                  Returns true if a Spaceball is
  65.  *                  found, false if not.
  66.  *     sbEvent(dev, val)     - Call after every call to qread
  67.  *                   Returns 1 if it is a Spaceball event.
  68.  *                   Note: it needs device INPUTCHANGE
  69.  *                   to correctly run the Spaceball I/F.
  70.  *     sbMapply( ... )        - Apply changes required.
  71.  * 
  72.  *     sbStatus()        - Return a status string to be 
  73.  *                   displayed
  74.  *     sbPrmpt()        - Prompt Spaceball for a new reading.
  75.  * 
  76.  *     The user must supply two routines, they are spcbal_do and spchome.
  77.  * These functions are specific to each system and can be handled differently
  78.  * for each system.
  79.  *
  80.  *    The user provided routines can be coded as follows.
  81.  * ******************************************************************
  82.  * / * ==================== spcbal_do ========================== * /
  83.  * / * This routine is called by sbinteg.c when a Spaceball event has
  84.  *   * occurred.  This routine will use this information to manipulate
  85.  *   * a viewing matrix.
  86.  *   * /
  87.  * spcbal_do( rotvec, rotscale, transvec, transcale )
  88.  * Coord     rotvec[ 3 ];
  89.  * float    rotscale;
  90.  * Coord    transvec[ 3 ];
  91.  * float    transcale;
  92.  * {
  93.  *
  94.  *    / * Apply the incremental rotation and translation to the
  95.  *      * viewing matrix
  96.  *      * /
  97.  *     sbMapply( 
  98.  *        view_matrix,    / * The viewing matrix         * /
  99.  *         fov,        / * The field of view        * / 
  100.  *        aspect,     / * The aspect ratio        * /
  101.  *        objrad,        / * The object radius        * / 
  102.  *         rotvec,     / * Passed Rotation vector    * /
  103.  *         rotscale,     / * Passed Rotation scale    * /
  104.  *        transvec,    / * Passed Translation vector    * /
  105.  *        transcale    / * Passed Translation scale    * /
  106.  *    );
  107.  *
  108.  *    / * Screen update required flag needs to be set * /
  109.  *    screen_update_required = TRUE;
  110.  *    
  111.  * }
  112.  *
  113.  * / * ====================== spchome ====================== * /
  114.  * / * This routine is called when the user requests that the image
  115.  *   * is reset to the initial position.  This usually means setting
  116.  *   * the viewing matrix to the identity.
  117.  *   * /
  118.  * spchome()
  119.  * {
  120.  *    int    i, j;
  121.  *    / * Set the view matrix to the identity * /
  122.  *    for ( i = 0; i < 4; i ++ ) {
  123.  *        for ( j = 0; j < 4; j ++ ) {
  124.  *            view_matrix[ i ][ j ] = ( i == j );
  125.  *        }
  126.  *    }
  127.  * }
  128.  *
  129.  * ******************************************************************
  130.  *
  131.  * This module provides default functionality for the Spaceball buttons.
  132.  * This functionality can be changed by the user, however it is
  133.  * recommended that these are not changed to allow users to switch
  134.  * between applications without requiring to go through a new
  135.  * learning curve.
  136.  *
  137.  *
  138.  * The buttons are set to provide the following functionality.
  139.  *
  140.  *    SBBUT1 - Spacball keypad 1
  141.  *    SELECT TRANSLATION MODE/SET TRANSLATION SCALE FACTOR
  142.  *    press one    - Turn translations off
  143.  *              One beep.
  144.  *    press two    - Select dominant translation ( This
  145.  *              allows you to select the largest absolute
  146.  *              element of the translation vector - it will
  147.  *              provide pure x y or z translations.
  148.  *              Two beeps.
  149.  *    press three    - Turn translations back on.
  150.  *              Three beeps.
  151.  *    pressed directly after pressing buttons 5 or 6 sets the
  152.  *    scale factor for translations.
  153.  *
  154.  *    SBBUT2 - Spaceball keypad 2
  155.  *    SELECT ROTATION MODE/SET ROTATION SCALE FACTOR
  156.  *    press one    - Turn rotations off
  157.  *              One beep.
  158.  *    press two    - Select dominant rotation ( This
  159.  *              allows you to select the largest absolute
  160.  *              element of the rotation vector - it will
  161.  *              provide pure rotations about the x y or z 
  162.  *              axes.
  163.  *              Two beeps.
  164.  *    press three    - Turn rotations back on.
  165.  *              Three beeps.
  166.  *    pressed directly after pressing buttons 5 or 6 sets the
  167.  *    scale factor for rotations.
  168.  *
  169.  *    SBBUT3 - Spaceball keypad 3
  170.  *    SELECT MOUSE/SPACEBALL MODE
  171.  *    Toggle between mouse and Spaceball mode.
  172.  *    When entering mouse mode there will be three short beeps
  173.  *    when entering spaceball mode there will be one short beep.
  174.  *
  175.  *    SBBUT4 - Spaceball keypad 4
  176.  *    SELECT OBJECT/EYEPOINT CONTROL MODE
  177.  *    Toggle between object control and eyepoint control modes.
  178.  *    Object control allows the user to manipulate the view as if
  179.  *    the object is at the Spaceball, i.e. exerting a force at the
  180.  *    Spaceball is like exerting a force on the object.
  181.  *    Eyepoint control allows the user to manipulate the eyepoint
  182.  *    so that the forces exerted at the Spaceball are placed on
  183.  *     the eyepoint of the system.
  184.  *
  185.  *    SBBUT5 - Spaceball keypad 5
  186.  *    SET LARGER SCALE FACTOR
  187.  *    This allows the changing of rotation and translation scale 
  188.  *    factors.  Every hit to this key sets the scale factor up
  189.  *    by 20%.  After striking this key a number of times you may
  190.  *    choose it to apply to translations or rotations by 
  191.  *    preesing keypad 1 or 2.    
  192.  *
  193.  *    SBBUT6 - Spaceball keypad 6
  194.  *    SET SMALLER SCALE FACTOR
  195.  *    This allows the changing of rotation and translation scale 
  196.  *    factors.  Every hit to this key sets the scale factor down
  197.  *    by 20%.  After striking this key a number of times you may
  198.  *    choose it to apply to translations or rotations by 
  199.  *    preesing keypad 1 or 2.    
  200.  *
  201.  *    SBBUT7 - Spaceball keypad 7
  202.  *    --- UNUSED ---
  203.  *    You may find an interesting function for this key !
  204.  *
  205.  *    SBBUT8 - Spaceball keypad 8
  206.  *    REZERO SPACEBALL
  207.  *    This will ask Spaceball to rezero itself. This can be
  208.  *    used to eliminate drift.
  209.  *    
  210.  *    SBPICK - Spaceball pick button
  211.  *    Reset the image to original position.
  212.  *
  213.  * 
  214.  * ****************************************************************  
  215.  *
  216.  *    To display a Spaceball status message you may call the
  217.  * function sbStatus() to return a pointer to a string containing the
  218.  * current Spaceball status.  This message should be displayed on every
  219.  * screen update.
  220.  *
  221.  * By using this module you can make the integration of Spaceball into
  222.  * your code very quickly and easily.  Feel free to modify this module
  223.  * to change the functionality to suit your application.
  224.  *
  225.  */
  226.  
  227. #ifdef spaceball
  228.  
  229. /* ===================== INCLUDE FILES ======================= */
  230.  
  231. #include <math.h>
  232. #include <gl.h>
  233. #include <gl/device.h>
  234. #include <gl/spaceball.h>
  235.  
  236. /* ===================== MACROS ============================== */
  237.  
  238. #define veclength( v )  sqrt( v[0]*v[0] + v[1]*v[1] + v[2]*v[2] )
  239.  
  240. #define lengthof( array )       (sizeof( array )/sizeof( array[0] ))
  241.  
  242. #define SBM_ON        0
  243. #define SBM_OFF        1
  244. #define SBM_DOMINANT    2
  245.  
  246. /* ===================== SBXEVENT STRUCT ===================== */
  247. /* This structure is used to make a table of devices to be used
  248.  * by this module
  249.  */
  250.  
  251. typedef struct {
  252.     short      dev;        /* Device number */
  253.     char    * str;        /* Textual rep of dev - for debug if reqd*/
  254.     short      ret_val;    /* Set to true if it is a spaceball event */
  255.     int          ( * func )();    /* Function to call when event occurs */
  256.     int          param;    /* Parameter to the function */
  257. } sbx_event;
  258.  
  259. /* ===================== SBX_TABLE =========================== */
  260. /* Every device that needs to be captured is represented in this 
  261.  * table
  262.  */
  263.  
  264. /* The following is a set of function declarations that will be
  265.  * used in the table to perform actions from events processed. */
  266. int    sbx_period();
  267. int    sbx_t();
  268. int    sbx_r();
  269. int    sbx_k1();
  270. int    sbx_k2();
  271. int    sbx_k3();
  272. int    sbx_k4();
  273. int    sbx_k5();
  274. int    sbx_k6();
  275. int    sbx_k7();
  276. int    sbx_k8();
  277. int    sbx_pick();
  278. int    sbx_inputchange();
  279.  
  280. sbx_event    sbx_table[] = {
  281. {
  282.     SBPERIOD,    /* short  dev;    Device number */
  283.     "SBPERIOD",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  284.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  285.     sbx_period,    /* int ( * func )();Function to call when event occurs */
  286.     0        /* int  param;Parameter to the function */
  287. },
  288. {
  289.     SBTX,    /* short  dev;    Device number */
  290.     "SBTX",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  291.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  292.     sbx_t,    /* int ( * func )();Function to call when event occurs */
  293.     0        /* int  param;Parameter to the function */
  294. },
  295. {
  296.     SBTY,    /* short  dev;    Device number */
  297.     "SBTY",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  298.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  299.     sbx_t,    /* int ( * func )();Function to call when event occurs */
  300.     1        /* int  param;Parameter to the function */
  301. },
  302. {
  303.     SBTZ,    /* short  dev;    Device number */
  304.     "SBTZ",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  305.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  306.     sbx_t,    /* int ( * func )();Function to call when event occurs */
  307.     2        /* int  param;Parameter to the function */
  308. },
  309. {
  310.     SBRX,    /* short  dev;    Device number */
  311.     "SBRX",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  312.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  313.     sbx_r,    /* int ( * func )();Function to call when event occurs */
  314.     0        /* int  param;Parameter to the function */
  315. },
  316. {
  317.     SBRY,    /* short  dev;    Device number */
  318.     "SBRY",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  319.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  320.     sbx_r,    /* int ( * func )();Function to call when event occurs */
  321.     1        /* int  param;Parameter to the function */
  322. },
  323. {
  324.     SBRZ,    /* short  dev;    Device number */
  325.     "SBRZ",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  326.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  327.     sbx_r,    /* int ( * func )();Function to call when event occurs */
  328.     2        /* int  param;Parameter to the function */
  329. },
  330. {
  331.     SBBUT1,    /* short  dev;    Device number */
  332.     "SBBUT1",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  333.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  334.     sbx_k1,    /* int ( * func )();Function to call when event occurs */
  335.     0          /* int  param;Parameter to the function */
  336. },
  337. {
  338.     SBBUT2,    /* short  dev;    Device number */
  339.     "SBBUT2",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  340.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  341.     sbx_k2,    /* int ( * func )();Function to call when event occurs */
  342.     0          /* int  param;Parameter to the function */
  343. },
  344. {
  345.     SBBUT3,    /* short  dev;    Device number */
  346.     "SBBUT3",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  347.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  348.     sbx_k3,    /* int ( * func )();Function to call when event occurs */
  349.     0          /* int  param;Parameter to the function */
  350. },
  351. {
  352.     SBBUT4,    /* short  dev;    Device number */
  353.     "SBBUT4",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  354.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  355.     sbx_k4,    /* int ( * func )();Function to call when event occurs */
  356.     0          /* int  param;Parameter to the function */
  357. },
  358. {
  359.     SBBUT5,    /* short  dev;    Device number */
  360.     "SBBUT5",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  361.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  362.     sbx_k5,    /* int ( * func )();Function to call when event occurs */
  363.     0          /* int  param;Parameter to the function */
  364. },
  365. {
  366.     SBBUT6,    /* short  dev;    Device number */
  367.     "SBBUT6",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  368.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  369.     sbx_k6,    /* int ( * func )();Function to call when event occurs */
  370.     0          /* int  param;Parameter to the function */
  371. },
  372. {
  373.     SBBUT7,    /* short  dev;    Device number */
  374.     "SBBUT7",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  375.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  376.     sbx_k7,    /* int ( * func )();Function to call when event occurs */
  377.     0          /* int  param;Parameter to the function */
  378. },
  379. {
  380.     SBBUT8,    /* short  dev;    Device number */
  381.     "SBBUT8",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  382.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  383.     sbx_k8,    /* int ( * func )();Function to call when event occurs */
  384.     0          /* int  param;Parameter to the function */
  385. },
  386. {
  387.     SBPICK,    /* short  dev;    Device number */
  388.     "SBPICK",    /* char    * str;    Textual rep of dev - for debug if reqd*/
  389.     1,        /* short  ret_val;Set to true if it is a spaceball event */
  390.     sbx_pick,    /* int ( * func )();Function to call when event occurs */
  391.     0          /* int  param;Parameter to the function */
  392. },
  393. {
  394.     INPUTCHANGE,/* short  dev;    Device number */
  395.     "INPUTCHANGE",/* char* str;    Textual rep of dev - for debug if reqd*/
  396.     0,        /* short  ret_val;Set to true if it is a spaceball event */
  397.     sbx_inputchange,/* int ( * func )();Function to call when event occurs */
  398.     0          /* int  param;Parameter to the function */
  399. },
  400. };    /* sbx_table */
  401.  
  402. /* Set to true if spaceball is open */
  403. int    sbv_open = 0;
  404.  
  405. /* ======================= sbInit ========================== */
  406. /* Check to see that Spaceball exists and queue all the 
  407.  * Spaceball devices.
  408.  */
  409.  
  410. sbInit()
  411. {
  412.     int         i;
  413.  
  414.     /* If it is there then assume it exists */
  415.     if ( sbv_open ) {
  416.     return 1;
  417.     }
  418.  
  419.     /* Make sure that a spaceball exists */
  420.     if ( !sbexists() ) {
  421.     return 0;
  422.     }
  423.  
  424.     sbv_open = 1;
  425.     /* queue all the devices in the table */
  426.     for ( i = 0; i < lengthof( sbx_table ); i ++ ) {
  427.         qdevice( sbx_table[ i ].dev );
  428.     }
  429.  
  430.     return 1;
  431. }
  432.  
  433. /* ======================= sbEvent ======================== */
  434. /* sbEvent must be called for every event that needs to be trapped
  435.  * by this module.  It can be called for every event recieved
  436.  * and will return true if the event is a special Spaceball
  437.  * event and will return false if is not.  Note however that
  438.  * it will use the events from the INPUTCHANGE device but will
  439.  * return false.
  440.  */
  441. sbEvent( dev, val )
  442. short    dev;
  443. short    val;
  444. {
  445.     int        i;
  446.  
  447.     /* Make sure that the Spaceball is open */
  448.     if ( ! sbv_open ) {
  449.     return 0;
  450.     }
  451.  
  452.     /* Scan through the table of devices to check for devices
  453.      * to be trapped.
  454.      */
  455.     for ( i = 0; i < lengthof( sbx_table ); i ++ ) {
  456.     if ( dev == sbx_table[ i ].dev ) {
  457.         (* (sbx_table[ i ].func))( dev, val, sbx_table[ i ].param );
  458.         return sbx_table[ i ].ret_val;
  459.     }
  460.     }
  461.  
  462.     /* Was not a Saceball event */
  463.     return 0;
  464. }
  465.  
  466.  
  467.  
  468. /* ================== Values ========================= */
  469.  
  470. #define IDENTITY_MATRIX         { \
  471.         { 1, 0, 0, 0 }, \
  472.         { 0, 1, 0, 0 }, \
  473.         { 0, 0, 1, 0 }, \
  474.         { 0, 0, 0, 1 }  \
  475.   }
  476.  
  477. int    sbv_prompt = 1;    /* True if a prompt is required */
  478.  
  479. int    sbv_mask = 0;    /* Used to determine which events are still */
  480.             /* requred to complete a block of data from */
  481.             /* Spaceball.                    */
  482.  
  483.  
  484. typedef struct {    /* used for type casting */
  485.     Matrix    mat;
  486. } sbt_mat;
  487.  
  488. /* =============== Values from Spaceball ================== */
  489. int    sbv_period;    /* Period between readings */
  490.  
  491. Coord    sbv_t[ 3 ];    /* Translation vector */
  492.  
  493. Coord    sbv_r[ 3 ];    /* Rotation axis vector */
  494.  
  495. Coord    sbv_tvec[ 3 ];    /* Scaled trsnalation vector */
  496.  
  497. int    sbv_roff;    /* Set to true if rots are off */
  498. int    sbv_toff;    /* Set to true if trans are off */
  499.  
  500. float    sbr_translation_rate = .006;
  501. float      sbr_rotation_rate = .015;
  502.  
  503. int    sbv_tranmode = 0;/* Translation mode */
  504. int    sbv_rotmode = 0; /* Rotation mode */
  505.  
  506. int    sbv_eyemode = 0;    /* Mode of operation */
  507.  
  508. int    sbv_status_update = 1;    /* Does the status line need update ? */
  509.  
  510. int    sbv_stscale = 0;    /* True when change scale seleceted */
  511.  
  512. float    sbv_adjust_scale = 1.0;    /* The factor to change a scale by */
  513.  
  514. int    sbv_inmouse = 0;    /* Assume we are in Spaceball mode */
  515.  
  516. /* ================== Functions ======================= */
  517.  
  518. /* Called when a SBPERIOD event occurs */
  519. int    sbx_period( dev, val, arg )
  520. short    dev;
  521. short    val;
  522. int    arg;
  523. {
  524.     sbv_period = val;
  525.     sbv_check( 1, 0 );
  526. }
  527.  
  528.  
  529. /* Called for events from devices SBTX, SBTY, SBTZ */
  530. int    sbx_t( dev, val, arg )
  531. short   dev;
  532. short   val;
  533. int     arg;
  534. {
  535.     /* Check to see if translations have been set to off */
  536.     if ( ! sbv_toff ) {
  537.     sbv_t[ arg ] = ldexp( (float) val, -14 );
  538.     } else {
  539.     sbv_t[ arg ] = 0.0;
  540.     }
  541.  
  542.     sbv_check( 2 << arg, 0 );
  543. }
  544.  
  545.  
  546. /* Called for events from devices SBRX, SBRY, SBRZ */
  547. int    sbx_r( dev, val, arg )
  548. short   dev;
  549. short   val;
  550. int     arg;
  551. {
  552.     /* Check to see if rotations have been set to off */
  553.     if ( ! sbv_roff ) {
  554.     sbv_r[ arg ] = ldexp( (float) val, -14 );
  555.     } else {
  556.     sbv_r[ arg ] = 0.0;
  557.     }
  558.  
  559.     sbv_check( (0x10 << arg), arg == 2 );
  560. }
  561.  
  562. /* This function detects when all expected information from
  563.  * Spaceball has arrived.  When this happens it will call
  564.  * spcbal_do(...)
  565.  */
  566. sbv_check( val, complete )
  567. int    val;
  568. int    complete;
  569. {
  570.     float    rscale;
  571.     float    tscale;
  572.  
  573.     /* Did we get all the values ? */
  574.     if ((sbv_mask |= val) == 0x7f) { 
  575.  
  576.     /* Was the last value rot z ? */
  577.     if ( !complete ) {
  578.         return;
  579.     }
  580.  
  581.     sbv_prompt = 1;        /* Send a prompt next time */
  582.     
  583.     /* If both rotations and translations are off then */
  584.     /* don't bother doing any work */
  585.     if ( (sbv_rotmode == SBM_OFF) && (sbv_tranmode == SBM_OFF) ) {
  586.         return;
  587.     }
  588.  
  589.     /* Calculate scale factors */
  590.     tscale = sbr_translation_rate * sbv_period;    
  591.     rscale = sbr_rotation_rate * sbv_period;    
  592.  
  593.     /* Check to see if we have dominant mode */
  594.     if (sbv_rotmode == SBM_DOMINANT) {
  595.         sbx_dominant( sbv_r );
  596.     }
  597.     if (sbv_tranmode == SBM_DOMINANT) {
  598.         sbx_dominant( sbv_t );
  599.     }
  600.  
  601.  
  602.     spcbal_do( sbv_r, rscale, sbv_t, tscale );  /* Call the application */
  603.  
  604.     }
  605. }
  606.  
  607.  
  608. /* Apply the Spaceball stuff to a matrix */
  609. sbMapply( mat, fov, aspect, objrad, sbv_r, rscale, sbv_t, tscale )
  610. Matrix        mat;
  611. float        fov;        /* Field of view */
  612. float        aspect;        /* Aspect ratio  */
  613. float        objrad;        /* Object radius */
  614. Coord          * sbv_r;
  615. float        rscale;
  616. Coord          * sbv_t;
  617. float        tscale;
  618. {
  619.     Coord    vec[ 3 ];
  620.  
  621.     vec[ 0 ] = mat[ 3 ][ 0 ];
  622.     vec[ 1 ] = mat[ 3 ][ 1 ];
  623.     vec[ 2 ] = mat[ 3 ][ 2 ];
  624.  
  625.     mat[ 3 ][ 0 ] = 0.0;
  626.     mat[ 3 ][ 1 ] = 0.0;
  627.     mat[ 3 ][ 2 ] = 0.0;
  628.  
  629.     sbMvapply( mat, vec, fov, aspect, objrad, sbv_r, rscale, sbv_t, tscale );
  630.  
  631.     mat[ 3 ][ 0 ] = vec[ 0 ];
  632.     mat[ 3 ][ 1 ] = vec[ 1 ];
  633.     mat[ 3 ][ 2 ] = vec[ 2 ];
  634.     
  635. }
  636.  
  637. /* utility routine to scale a vector
  638.  */
  639. sbx_scalevec( result, source, scale )
  640. Coord        * result;
  641. Coord        * source;
  642. float          scale;
  643. {
  644.     int        i;
  645.  
  646.     /* do it for all the elements */
  647.     for ( i = 0; i < 3; i ++ ) { 
  648.     * (result ++) = * (source ++) * scale;
  649.     }
  650. }
  651.     
  652. /* Choose dominant component
  653.  */
  654. sbx_dominant( vec )
  655. float vec[3];
  656. {
  657.     if (fabs( vec[0] ) > fabs( vec[1] )) {
  658.     vec[1] = 0.0;
  659.     if (fabs( vec[0] ) > fabs( vec[2] ))
  660.         vec[2] = 0.0;
  661.     else
  662.         vec[0] = 0.0;
  663.     } else {
  664.     vec[0] = 0.0;
  665.     if (fabs( vec[1] ) > fabs( vec[2] ))
  666.         vec[2] = 0.0;
  667.     else
  668.         vec[1] = 0.0;
  669.     }
  670. }
  671.  
  672. /* utility routines to handle wierd matrix multiplies
  673.  */
  674. typedef struct {
  675.     Matrix    m;
  676. } mattype;
  677.  
  678. static
  679. void
  680. m3x3mult( m1, m2, prod )
  681. mattype        *m1, *m2, *prod;
  682. {
  683.         register int     row, col;
  684.         mattype     temp[1];
  685.  
  686.     * temp = * prod;
  687.  
  688.         for(row=0 ; row<3 ; row++) {
  689.                 for(col=0 ; col<3 ; col++) {
  690.                         temp->m[row][col] = 
  691.                 m1->m[row][0] * m2->m[0][col]
  692.                               + m1->m[row][1] * m2->m[1][col]
  693.                               + m1->m[row][2] * m2->m[2][col]
  694.                               + m1->m[row][3] * m2->m[3][col];
  695.         }
  696.     }
  697.  
  698.     * prod = * temp;
  699. }
  700.  
  701. static
  702. void
  703. m4x3mult( m1, m2, prod )
  704. mattype        *m1, *m2, *prod;
  705. {
  706.         register int     row, col;
  707.         mattype     temp[1];
  708.  
  709.     * temp = * prod;
  710.  
  711.         for(row=0 ; row<4 ; row++) {
  712.                 for(col=0 ; col<3 ; col++) {
  713.                         temp->m[row][col] = 
  714.                 m1->m[row][0] * m2->m[0][col]
  715.                               + m1->m[row][1] * m2->m[1][col]
  716.                               + m1->m[row][2] * m2->m[2][col]
  717.                               + m1->m[row][3] * m2->m[3][col];
  718.         }
  719.     }
  720.  
  721.     * prod = * temp;
  722. }
  723.  
  724.  
  725. /* Same as sbMapply except works on a rotation matrix and a translation
  726.  * vector
  727.  */
  728. sbMvapply( mat, vec, fov, aspect, objrad, rotvec, rscale, tranvec, tscale )
  729. Matrix        mat;        /* Orientation of object */
  730. Coord          * vec;        /* Position of object */
  731. float           fov;            /* Field of view */
  732. float           aspect;         /* Aspect ratio  */
  733. float           objrad;         /* Object radius */
  734. Coord         * rotvec;
  735. float           rscale;
  736. Coord         * tranvec;
  737. float           tscale;
  738. {
  739.  
  740.     float    v[ 3 ];
  741.     float    objdist;
  742.     float    bentfactor;
  743.     Matrix     delta_matrix;
  744.     Coord    sbv_t[ 3 ];
  745.     Coord    sbv_r[ 3 ];
  746.     Coord    bentvec[ 3 ];
  747.  
  748.     /* Scale the vectors */
  749.     sbx_scalevec( sbv_r, rotvec, rscale );
  750.     sbx_scalevec( sbv_t, tranvec, tscale );
  751.  
  752.     if ( sbv_eyemode ) {
  753.     vec[ 0 ] -= sbv_t[ 0 ] * objrad;
  754.     vec[ 1 ] -= sbv_t[ 1 ] * objrad;
  755.     vec[ 2 ] += sbv_t[ 2 ] * objrad;
  756.     } else {
  757.  
  758.     v[2] = vec[ 2 ] - 1.1;
  759.  
  760.     if ( v[2] > -objrad ) {
  761.         vec[ 0 ] += sbv_t[ 0 ] * objrad;
  762.         vec[ 1 ] += sbv_t[ 1 ] * objrad;
  763.         vec[ 2 ] -= sbv_t[ 2 ] * objrad;
  764.     } else {
  765.         v[ 0 ] = vec[ 0 ];
  766.         v[ 1 ] = vec[ 1 ];
  767.         objdist = veclength( v );
  768.         bentfactor = sbv_t[2] / objdist;
  769.  
  770.         bentvec[ 0 ] = v[ 0 ] * bentfactor;
  771.         bentvec[ 1 ] = v[ 1 ] * bentfactor;
  772.         bentvec[ 2 ] = v[ 2 ] * bentfactor;
  773.  
  774.         vec[ 0 ] += objdist * (sbv_t[0] + bentvec[0]);
  775.         vec[ 1 ] += objdist * (sbv_t[1] + bentvec[1]);
  776.         vec[ 2 ] += objdist * (           bentvec[2]);
  777.     }
  778.     }
  779.  
  780.     /* Make sure that rotations are on */
  781.     if (sbv_roff) {
  782.         return;
  783.     }
  784.  
  785.     if ( sbv_eyemode ) {
  786.     
  787.     rotarbaxis( -1.0, -sbv_r[0], -sbv_r[1], sbv_r[2], delta_matrix );
  788.  
  789.     mat[3][0] = vec[0];
  790.     mat[3][1] = vec[1];
  791.     mat[3][2] = vec[2] - 0.1;
  792.  
  793.     m4x3mult( mat, delta_matrix, mat );
  794.  
  795.     vec[0] = mat[3][0];
  796.     vec[1] = mat[3][1];
  797.     vec[2] = mat[3][2] + 0.1;
  798.  
  799.     mat[3][0] = 0.0;
  800.     mat[3][1] = 0.0;
  801.     mat[3][2] = 0.0;
  802.     } else {
  803.     /* Object mode */
  804.  
  805.     rotarbaxis( 1.0, -sbv_r[0], -sbv_r[1], sbv_r[2], delta_matrix );
  806.  
  807.     m3x3mult( mat, delta_matrix, mat );
  808.     }
  809.  
  810. }
  811.  
  812.  
  813. #ifdef EULERS
  814. /* Same as sbMvapply except works on Euler rotation parameters and a 
  815.  * translation vector
  816.  */
  817. sbEvapply( e, vec, fov, aspect, objrad, rotvec, rscale, tranvec, tscale )
  818. Coord          * e;        /* Orientation of object */
  819. Coord          * vec;        /* Position of object */
  820. float           fov;            /* Field of view */
  821. float           aspect;         /* Aspect ratio  */
  822. float           objrad;         /* Object radius */
  823. Coord         * rotvec;
  824. float           rscale;
  825. Coord         * tranvec;
  826. float           tscale;
  827. {
  828.  
  829.     float    v[ 3 ];
  830.     float    objdist;
  831.     float    bentfactor;
  832.     Matrix     delta_matrix;
  833.     Coord    sbv_t[ 3 ];
  834.     Coord    sbv_r[ 3 ];
  835.     Coord    bentvec[ 3 ];
  836.     Coord    ex[ 4 ];
  837.     Matrix    mat;
  838.  
  839.     /* Scale the vectors */
  840.     sbx_scalevec( sbv_r, rotvec, rscale );
  841.     sbx_scalevec( sbv_t, tranvec, tscale );
  842.  
  843.     if ( sbv_eyemode ) {
  844.     vec[ 0 ] -= sbv_t[ 0 ] * objrad;
  845.     vec[ 1 ] -= sbv_t[ 1 ] * objrad;
  846.     vec[ 2 ] += sbv_t[ 2 ] * objrad;
  847.     } else {
  848.     v[2] = vec[ 2 ] - 1.1;
  849.  
  850.     if ( v[2] > -objrad ) {
  851.         vec[ 0 ] += sbv_t[ 0 ] * objrad;
  852.         vec[ 1 ] += sbv_t[ 1 ] * objrad;
  853.         vec[ 2 ] -= sbv_t[ 2 ] * objrad;
  854.     } else {
  855.         v[ 0 ] = vec[ 0 ];
  856.         v[ 1 ] = vec[ 1 ];
  857.         objdist = veclength( v );
  858.         bentfactor = sbv_t[2] / objdist;
  859.  
  860.         bentvec[ 0 ] = v[ 0 ] * bentfactor;
  861.         bentvec[ 1 ] = v[ 1 ] * bentfactor;
  862.         bentvec[ 2 ] = v[ 2 ] * bentfactor;
  863.  
  864.         vec[ 0 ] += objdist * (sbv_t[0] + bentvec[0]);
  865.         vec[ 1 ] += objdist * (sbv_t[1] + bentvec[1]);
  866.         vec[ 2 ] += objdist * (           bentvec[2]);
  867.     }
  868.     }
  869.  
  870.     /* Make sure that rotations are on */
  871.     if (sbv_roff) {
  872.         return;
  873.     }
  874.  
  875.     if ( sbv_eyemode ) {
  876.     
  877.     axistoeuler( -1.0, -sbv_r[0], -sbv_r[1], sbv_r[2], ex );
  878.  
  879.     add_eulers( ex, e, e );
  880.  
  881.     rotarbaxis( -1.0, -sbv_r[0], -sbv_r[1], sbv_r[2], delta_matrix );
  882.  
  883.     *((mattype *) mat) = *((mattype *) delta_matrix);
  884.  
  885.     mat[3][0] = vec[0];
  886.     mat[3][1] = vec[1];
  887.     mat[3][2] = vec[2] - 1.1;
  888.  
  889.     m4x3mult( mat, delta_matrix, mat );
  890.  
  891.     vec[0] = mat[3][0];
  892.     vec[1] = mat[3][1];
  893.     vec[2] = mat[3][2] + 1.1;
  894.  
  895.     } else {
  896.     /* Object mode */
  897.  
  898.     axistoeuler( 1.0, -sbv_r[0], -sbv_r[1], sbv_r[2], ex );
  899.  
  900.     add_eulers( ex, e, e );
  901.     }
  902.  
  903. }
  904.  
  905. /* axistoeuler will take an axis rotation vector and create an equivalent
  906.  * Euler rotation vector.
  907.  */
  908. axistoeuler( scale, x_rotvec, y_rotvec, z_rotvec, e )
  909. float      scale;
  910. Coord      x_rotvec;
  911. Coord      y_rotvec;
  912. Coord      z_rotvec;
  913. Coord    * e;
  914. {
  915.     float
  916.       radians, radians2, invlength, myx_rotvec, myy_rotvec, myz_rotvec,
  917.       sin02, cos02;
  918.  
  919.     /* Find the length of the vector */
  920.     radians = sqrt( x_rotvec*x_rotvec + y_rotvec*y_rotvec + z_rotvec*z_rotvec );
  921.  
  922.     /* If the vector has zero length - return the identity matrix */
  923.     if (radians == 0.0) {
  924.         int    i;
  925.     int    j;
  926.  
  927.     e[0] = 0.0;
  928.     e[1] = 0.0;
  929.     e[2] = 0.0;
  930.     e[3] = 1.0;
  931.  
  932.         return;
  933.     }
  934.  
  935.     /* normalize the rotation vector */
  936.     invlength = 1 / radians;
  937.     myx_rotvec = x_rotvec * invlength;
  938.     myy_rotvec = y_rotvec * invlength;
  939.     myz_rotvec = z_rotvec * invlength;
  940.     radians2  = radians * scale/2;   /* calculate the number of radians */
  941.  
  942.     /* Do all the smart stuff */
  943.     sin02 = sin( radians2 );
  944.     cos02 = cos( radians2 );
  945.  
  946.     e[0] = myx_rotvec * sin02;
  947.     e[1] = myy_rotvec * sin02;
  948.     e[2] = myz_rotvec * sin02;
  949.     e[3] = cos02;
  950.  
  951. }
  952.  
  953. #endif
  954.  
  955.  
  956. /* Error handler */
  957. sbx_error( str )
  958. {
  959.     printf( "%s error : %s", __FILE__, str );
  960.     exit( 2 );
  961. }
  962.  
  963. /* ===================== sbx_k? ========================== */
  964. /* These routines handle keypad events.
  965.  */
  966.  
  967. int    sbx_k2( dev, val, arg )
  968. short   dev;
  969. short   val;
  970. int     arg;
  971. {
  972.     if ( val ) {
  973.  
  974.     if ( sbv_stscale ) {        /* if we are setting the scale */
  975.         sbr_rotation_rate *= sbv_adjust_scale;
  976.         sbv_adjust_scale = 1;    /* Initialize it to 0 */
  977.         sbv_stscale = 0;        /* Scale factor has been used */
  978.         sbv_status_update = 1;    /* Update the status line */
  979.         sbx_beep( 1 );
  980.         return;
  981.     }
  982.  
  983.     /* Rotation modes */
  984.     switch ( sbv_rotmode ) {
  985.         case SBM_ON : {
  986.         /* Rotations are on - turn them off */
  987.         sbv_rotmode = SBM_OFF;
  988.         sbv_roff = 1;
  989.         sbv_status_update = 1;        /* Update the status line */
  990.         sbx_beep( 1 );            /* do a type 1 beep */
  991.         break;
  992.         }
  993.         case SBM_OFF : {
  994.         /* Rotations are off - go to dominant mode */
  995.         sbv_rotmode = SBM_DOMINANT;
  996.         sbv_roff = 0;
  997.         sbv_status_update = 1;
  998.         sbx_beep( 2 );
  999.         break;
  1000.         }
  1001.             case SBM_DOMINANT : {
  1002.                 /* Rotations are off - go to dominant mode */
  1003.                 sbv_rotmode = SBM_ON;
  1004.                 sbv_roff = 0;
  1005.                 sbv_status_update = 1;
  1006.                 sbx_beep( 3 );
  1007.                 break;
  1008.             }
  1009.         default : {
  1010.         sbx_error( "Bug 1" );    /* Should never get here */
  1011.         break;
  1012.         }
  1013.     }
  1014.    } else {
  1015.     /* k2 has been released */
  1016.    }
  1017. }
  1018.  
  1019.  
  1020.  
  1021. int    sbx_k1( dev, val, arg )
  1022. short   dev;
  1023. short   val;
  1024. int     arg;
  1025. {
  1026.  
  1027.     if ( val ) {
  1028.  
  1029.     if ( sbv_stscale ) {        /* if we are setting the scale */
  1030.         sbr_translation_rate *= sbv_adjust_scale;
  1031.         sbv_adjust_scale = 1;    /* Initialize it to 0 */
  1032.         sbv_stscale = 0;        /* Scale factor has been used */
  1033.         sbv_status_update = 1;    /* Update the status line */
  1034.         sbx_beep( 1 );
  1035.         return;
  1036.     }
  1037.  
  1038.     /* Rotation modes */
  1039.     switch ( sbv_tranmode ) {
  1040.         case SBM_ON : {
  1041.         /* Rotations are on - turn them off */
  1042.         sbv_tranmode = SBM_OFF;
  1043.         sbv_toff = 1;
  1044.         sbv_status_update = 1;        /* Update the status line */
  1045.         sbx_beep( 1 );            /* do a type 1 beep */
  1046.         break;
  1047.         }
  1048.         case SBM_OFF : {
  1049.         /* Rotations are off - go to dominant mode */
  1050.         sbv_tranmode = SBM_DOMINANT;
  1051.         sbv_toff = 0;
  1052.         sbv_status_update = 1;
  1053.         sbx_beep( 2 );
  1054.         break;
  1055.         }
  1056.             case SBM_DOMINANT : {
  1057.                 /* Rotations are off - go to dominant mode */
  1058.                 sbv_tranmode = SBM_ON;
  1059.                 sbv_toff = 0;
  1060.                 sbv_status_update = 1;
  1061.                 sbx_beep( 3 );
  1062.                 break;
  1063.             }
  1064.         default : {
  1065.         sbx_error( "Bug 1" );    /* Should never get here */
  1066.         break;
  1067.         }
  1068.     }
  1069.    } else {
  1070.     /* k1 has been released */
  1071.    }
  1072. }
  1073.  
  1074.  
  1075.  
  1076. int    sbx_k3( dev, val, arg )
  1077. short   dev;
  1078. short   val;
  1079. int     arg;
  1080. {
  1081.     if ( val ) {
  1082.     /* go into mouse mode / spaceball mode */
  1083.     if ( ! sbv_inmouse ) {
  1084.         sbv_inmouse = 1;
  1085.         sbmouse();
  1086.         sbx_beep( 4 );
  1087.         sbv_status_update = 1;
  1088.     } else {
  1089.         sbv_inmouse = 0;
  1090.         sbspaceball();
  1091.         sbx_beep( 5 );
  1092.         sbv_status_update = 1;
  1093.     }
  1094.     } else {
  1095.     }
  1096. }
  1097.  
  1098.  
  1099.  
  1100. int    sbx_k4( dev, val, arg )
  1101. short   dev;
  1102. short   val;
  1103. int     arg;
  1104. {
  1105.     if ( val ) {
  1106.     /* go into mouse mode / spaceball mode */
  1107.     if ( sbv_eyemode ) {
  1108.         sbv_eyemode = 0;
  1109.         sbx_beep( 1 );
  1110.         sbv_status_update = 1;
  1111.     } else {
  1112.         sbv_eyemode = 1;
  1113.         sbx_beep( 2 );
  1114.         sbv_status_update = 1;
  1115.     }
  1116.     } else {
  1117.     }
  1118. }
  1119.  
  1120.  
  1121.  
  1122. int    sbx_k5( dev, val, arg )
  1123. short   dev;
  1124. short   val;
  1125. int     arg;
  1126. {
  1127.     if ( val ) {
  1128.         sbv_adjust_scale *= 1.2;    /* Initialize it to 0 */
  1129.         sbv_stscale = 1;            /* Scale factor has been used */
  1130.     sbv_status_update = 1;
  1131.     sbx_beep( 5 );            /* One short beep */
  1132.     } else {
  1133.     }
  1134.  
  1135. }
  1136.  
  1137.  
  1138.  
  1139. int    sbx_k6( dev, val, arg )
  1140. short   dev;
  1141. short   val;
  1142. int     arg;
  1143. {
  1144.     if ( val ) {
  1145.         sbv_adjust_scale /= 1.2;    /* Initialize it to 0 */
  1146.         sbv_stscale = 1;            /* Scale factor has been used */
  1147.     sbv_status_update = 1;
  1148.     sbx_beep( 5 );            /* One short beep */
  1149.     } else {
  1150.     }
  1151. }
  1152.  
  1153.  
  1154.  
  1155. int    sbx_k7( dev, val, arg )
  1156. short   dev;
  1157. short   val;
  1158. int     arg;
  1159. {
  1160. }
  1161.  
  1162.  
  1163.  
  1164. int    sbx_k8( dev, val, arg )
  1165. short   dev;
  1166. short   val;
  1167. int     arg;
  1168. {
  1169.     if ( val ) {
  1170.     sbrezero();
  1171.     sbx_beep( 1 );
  1172.     } else {
  1173.     }
  1174. }
  1175.  
  1176.  
  1177.  
  1178. int    sbx_pick( dev, val, arg )
  1179. short   dev;
  1180. short   val;
  1181. int     arg;
  1182. {
  1183.     if ( val ) {
  1184.     sbx_beep( 1 );
  1185.     spchome();    /* User supplied routine to reset view*/
  1186.     } else {
  1187.     }
  1188. }
  1189.  
  1190.  
  1191.  
  1192. int    sbx_inputchange( dev, val, arg )
  1193. short   dev;
  1194. short   val;
  1195. int     arg;
  1196. {
  1197.     if ( val ) {
  1198.     /* Whenever the window comes into view - prompt spaceball */
  1199.     /* This avoids a 1.5 sec delay                  */
  1200.     sbprompt();
  1201.     } else {
  1202.     }
  1203. }
  1204.  
  1205.  
  1206. char * sbx_other()
  1207. {
  1208.     static    char buffer[ 30 ];
  1209.  
  1210.     if ( sbv_stscale ) {
  1211.     sprintf( buffer, " Sensitivity[%7.3f]", sbv_adjust_scale);
  1212.     return buffer;
  1213.     } else {
  1214.     return "";
  1215.     }
  1216. }
  1217.  
  1218. /* Set up a table of control modes */
  1219. char    * sbx_mode[] = {
  1220.     "On",
  1221.     "Off",
  1222.     "Dominant"
  1223. };
  1224.  
  1225. /* ======================== sbSchng ============================== */
  1226. /* Return true if there has been a status line change
  1227.  * Call sbStatus to reset the flag.
  1228.  */
  1229.  
  1230. sbSchng()
  1231. {
  1232.     return sbv_status_update;
  1233. }
  1234.  
  1235. /* ======================== sbStatus ============================= */
  1236. /* Return a pointer to a string with a meaningfull status line 
  1237.  */
  1238. char    * sbStatus()
  1239. {
  1240.     static char        buffer[ 100 ];
  1241.  
  1242.     if ( ! sbv_open ) {
  1243.     sbv_status_update = 0;
  1244.     return "";
  1245.     }
  1246.  
  1247.     /* Check to see if we need to update the status */
  1248.     if ( sbv_status_update ) {
  1249.     sprintf( buffer,
  1250.         "Spaceball Status : Translations[%s] Rotations[%s] Mode[%s]%s",
  1251.         sbx_mode[ sbv_tranmode ],
  1252.         sbx_mode[ sbv_rotmode ],
  1253.         ( sbv_eyemode ? "Eyepoint" : "Object" ),
  1254.         sbx_other()
  1255.     );
  1256.     }
  1257.  
  1258.     return buffer;
  1259. }
  1260.  
  1261.  
  1262. /* Set up a beeping table */
  1263.  
  1264. char    * sbv_beep[] = {
  1265.     "Cc",
  1266.     "Cc",
  1267.     "CcCc",
  1268.     "CcCcCc",
  1269.     "AaAaAa",
  1270.     "Aa"
  1271. };
  1272.  
  1273. sbx_beep( n )
  1274. int    n;
  1275. {
  1276.     sbbeep( sbv_beep[ n ] );
  1277. }
  1278.  
  1279. /* ======================== sbPrmpt ============================= */
  1280. /* Prompt spaceball if required.
  1281.  * This should be called after ( or before ) every screen update.
  1282.  */
  1283. sbPrmpt()
  1284. {
  1285.     if ( sbv_open && sbv_prompt ) {
  1286.     sbv_prompt = 0;
  1287.     sbprompt();
  1288.     }
  1289. }
  1290.  
  1291. #endif spaceball
  1292.